////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////

#ifndef SYMBOLTREE_HPP
#define SYMBOLTREE_HPP

/******************************************************************************
 * Includes                                                                   *
 ******************************************************************************/

#include <map>
using std::map;

#include <set>
using std::set;

#include <string>
using std::string;

#include <ostream>
using std::ostream;

#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
using boost::dynamic_pointer_cast;

#include <boost/weak_ptr.hpp>
using boost::weak_ptr;

#include <boost/enable_shared_from_this.hpp>
using boost::enable_shared_from_this;

#include "ast/type.hpp"
#include "path.hpp"
#include "accesstype.hpp"
#include "codelocation.hpp"
#include "outputproducer.hpp"

class UnnamedSymbol;
class NamedSymbol;
class BlinderSymbol;

/******************************************************************************
 * SymbolTree Class                                                           *
 ******************************************************************************/

class SymbolTree : public enable_shared_from_this<SymbolTree> {
	public:
		
		// Virtual destructor
		virtual ~SymbolTree() {}
		
		// Path of symbol
		const Path& path() const;
		
		// Parent
		bool has_parent();
		shared_ptr<SymbolTree> parent();
		virtual void set_parent(shared_ptr<SymbolTree> parent);
		
		// Children
		bool has_child(const string& name);
		void insert_child(shared_ptr<SymbolTree> subtree);
		shared_ptr<NamedSymbol> get_child(const string& name);
		const set<shared_ptr<SymbolTree> >& children();
		
		// Root
		shared_ptr<SymbolTree> root();
		bool is_root();
		
		// Finding local symbols directly within this scope
		virtual bool has_local_symbol(const string& symbol) =0;
		virtual shared_ptr<NamedSymbol> get_local_symbol(const string& symbol) =0;
		
		// Finding symbols visible from this scope
		bool symbol_is_visible(const Path& symbol);
		shared_ptr<NamedSymbol> get_visible_symbol(const Path& symbol);
		
		// Is the symbol within the scope created by the other?
		bool is_in(shared_ptr<SymbolTree> other);
		
		// What is the function in which this symbol is scoped?
		shared_ptr<SymbolTree> containing_function();
		
		// Used vars bookkeeping
		void insert_used_var(shared_ptr<NamedSymbol> var);
		set<shared_ptr<NamedSymbol> > used_vars();
		set<shared_ptr<NamedSymbol> > used_vars_recursive();
		
		// Written vars bookkeeping
		void insert_written_var(shared_ptr<NamedSymbol> var);
		set<shared_ptr<NamedSymbol> > written_vars();
		set<shared_ptr<NamedSymbol> > written_vars_recursive();
		set<shared_ptr<NamedSymbol> > used_vars_recursive_without_functions();
		
		// Called function bookkeeping
		void insert_called_function(shared_ptr<NamedSymbol> function);
		set<shared_ptr<NamedSymbol> > called_functions();
		set<shared_ptr<NamedSymbol> > called_functions_recursive();
		
		// Streaming
		virtual ostream& report_to_stream(ostream& out);
		
		friend class UnnamedSymbol;
	
	protected:
		
		SymbolTree();
		
		unsigned unnamedScopeCount;
		
		Path _path;
	
	private:
		
		weak_ptr<SymbolTree>                  _parent;
		set<shared_ptr<SymbolTree> >          _children;
		map<string, shared_ptr<NamedSymbol> > _namedChildren;
		weak_ptr<SymbolTree>                  _root;
		
		set<weak_ptr<NamedSymbol> > _usedVars;
		set<weak_ptr<NamedSymbol> > _writtenVars;
		set<weak_ptr<NamedSymbol> > _calledFunctions;
};

/******************************************************************************
 * NamedSymbol Class                                                          *
 ******************************************************************************/

class NamedSymbol : public SymbolTree {
	public:
		
		// Constructors
		NamedSymbol(const string& name, const CodeLocation& declarationLocation, shared_ptr<Type> type, AccessType accessType, bool logical = false);
		NamedSymbol(const string& name, shared_ptr<Type> type, AccessType accessType, bool logical = false);
		
		// Name
		const string& name() const;
		
		// Scope
		Path scope() const;
		
		// Set parent implementation
		void set_parent(shared_ptr<SymbolTree> parent);
		
		// Get a symbol directly scoped within this symbol
		bool has_local_symbol(const string& symbol);
		shared_ptr<NamedSymbol> get_local_symbol(const string& symbol);
		
		// Location of declaration
		bool declaration_location_is_set() const;
		const CodeLocation& declaration_location() const;
		
		// Type
		shared_ptr<Type> type() const;
		
		// Access type
		AccessType access_type() const;
		
		// Logical var?
		bool logical() const;
		
		// Streaming
		ostream& report_to_stream(ostream& out);
	
	private:
		
		string _name;
		
		CodeLocation     _location;
		shared_ptr<Type> _type;
		AccessType       _accessType;
		bool             _logical;
};

/******************************************************************************
 * UnnamedSymbol Class                                                        *
 ******************************************************************************/

class UnnamedSymbol : public SymbolTree {
	public:
		
		void set_parent(shared_ptr<SymbolTree> parent);
		
		bool has_local_symbol(const string& symbol);
		shared_ptr<NamedSymbol> get_local_symbol(const string& symbol);
		
		void add_default_symbols();
};

/******************************************************************************
 * BlinderSymbol Class                                                        *
 ******************************************************************************/

class BlinderSymbol : public SymbolTree {
	public:
		
		bool has_local_symbol(const string& symbol);
		shared_ptr<NamedSymbol> get_local_symbol(const string& symbol);
};

/******************************************************************************
 * OutputProducer                                                             *
 ******************************************************************************/

class symboltable : public OutputProducer<Node> {
	public:
		
		symboltable(shared_ptr<Node> tree);
		
		operator string() const;
		
		ostream& send_to_stream(ostream& out) const;
	
	private:
		
		shared_ptr<Node> _tree;
};

#endif // SYMBOLTREE_HPP
